﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Cloak.NUnit;
using Cloak.Reflection;
using NUnit.Framework;
using Ruleweaver.Rules;

namespace Ruleweaver.Conditions.Schemas
{
	public class GetDeclaredConditionsForFieldPropertyMethod : Behavior
	{
		private DeclaredConditionSource _source;
		private IEnumerable<Condition> _conditions;

		protected override void Given()
		{
			_source = new FieldPropertyMethodConditionSource();
		}

		protected override void When()
		{
			_conditions = _source.GetConditions().ToList();
		}

		[Then]
		public void FieldConditionIsIncluded()
		{
			Assert.IsTrue(_conditions.Any(condition =>
				Check.That(condition.Rule)
				.Passes(rule => rule.Type == RuleType.Field)
				.Passes(rule => ((MemberRule) rule).Member == typeof(TargetType).GetField("TargetField"))));
		}

		[Then]
		public void PropertyConditionIsIncluded()
		{
			Assert.IsTrue(_conditions.Any(condition =>
				Check.That(condition.Rule)
				.Passes(rule => rule.Type == RuleType.Property)
				.Passes(rule => ((MemberRule) rule).Member == typeof(TargetType).GetProperty("TargetProperty"))));
		}

		[Then]
		public void MethodConditionIsIncluded()
		{
			Assert.IsTrue(_conditions.Any(condition =>
				Check.That(condition.Rule)
				.Passes(rule => rule.Type == RuleType.Method)
				.Passes(rule => ((MemberRule) rule).Member == typeof(TargetType).GetMethod("GetTarget"))));
		}

		private sealed class TargetType
		{
			public int TargetField = 1;

			public int TargetProperty { get; set; }

			public int GetTarget()
			{
				return 0;
			}
		}

		private sealed class FieldPropertyMethodConditionSource : DeclaredConditionSource
		{
			public override Type TargetType
			{
				get { return typeof(TargetType); }
			}

			protected override IEnumerable<IConditionDeclaration> GetDeclarations(MemberInfo member)
			{
				if(member.DeclaringType != typeof(object))
				{
					yield return new TestDeclaration(member);
				}
			}
		}

		private sealed class TestDeclaration : IConditionDeclaration
		{
			private readonly MemberInfo _member;

			internal TestDeclaration(MemberInfo member)
			{
				_member = member;
			}

			public IEnumerable<Condition> GetConditions(Type targetType)
			{
				var method = Reflect.Func<ICheckable<int>, Check<int>>(Checkable.IsPositive);

				yield return new Condition(Rule.Check(method), typeof(int), _member.Name);
			}
		}
	}
}